<template>
  <div style="height: 100%">

    <!--搜索框-->
    <a-form
        ref="defSearch"
        :model="search.def"
        :rules="search.defRules"
        layout="inline"
        class="search_form">

      <a-form-item label="楼层" name="floor" class="_130w">
        <a-select v-model:value="search.def.floor" placeholder="楼层">
          <a-select-option v-for="(item,index) in option.floor" :value=item.key>
            {{
              item.text
            }}
          </a-select-option>
        </a-select>
      </a-form-item>

      <a-form-item label="区域名称" name="name" class="_260w">
        <a-auto-complete
            v-model:value="search.def.name"
            placeholder="请输入区域名称"
            :allowClear="true"
            :options="option.autoSearchName"
            @search="(value) => { searchAsync_( 'name' ,value)}"
        >
          <template #option="item">
            <div @click="searchAsyncSelect_('name',item.name,item)" style="font-weight: bold"> {{ item.name }}</div>
          </template>
        </a-auto-complete>

      </a-form-item>


      <div style="flex: 1 1 0;"/>


      <a-form-item style="margin-right: 0">
        <a-button type="primary" @click="queryPage_">查询</a-button>
        <a-button style="margin-left: 10px" @click="()=>{ resetForm_('defSearch'); }">重置</a-button>
      </a-form-item>
      <!--      <el-form-item>-->
      <!--        <el-button type="primary" @click="queryPage_">查询</el-button>-->
      <!--        <el-button @click="()=>{ resetForm_('searchForm'); queryPage_() }">重置</el-button>-->
      <!--      </el-form-item>-->
      <!--    </el-form>-->

    </a-form>

    <!--区域地图-->
    <div class="fx_col fx_110" style="border: 1px solid #F0F0F0;height: 60vh;margin: 16px 16px 0 16px">

      <!--表格选项-->
      <div class="table_tools"
           style="height:50px;width:100%;padding: 0 16px ;background: #FFFFFF;border-bottom: 1px solid #F0F0F0; align-items: center">

        <div style="color: rgba(0,0,0,.85);font-size: 16px;">区域地图</div>

        <div style="flex: 1 1 0"></div>

        <div>
          <span style="font-size: 14px;color: #000000;margin-right: 3px">报警提示</span>
          <a-switch v-model:checked="show.warn" checked-children="开启"
                    un-checked-children="关闭"/>
        </div>

        <a-divider type="vertical"/>

        <div>
          <span style="font-size: 14px;color: #000000;margin-right: 3px">人员位置</span>
          <a-switch v-model:checked="show.people" @change="changeShoPeople" checked-children="开启"
                    un-checked-children="关闭"/>
        </div>

        <a-divider type="vertical"/>

        <div>
          <span style="font-size: 14px;color: #000000;margin-right: 3px">轨迹</span>
          <a-switch v-model:checked="show.track" @change="changeShoTrack" checked-children="开启"
                    un-checked-children="关闭"/>
        </div>

      </div>

      <!--canvas-->
      <div class="fx_110" ref="canvasSize" style="background: #FFFFFF;" @mouseenter="mouseenter"
           @mouseleave="mouseleave">
        <canvas id="canvas" style="padding: 10px"/>
      </div>

    </div>

    <!--区域信息-->
    <a-row style="background: #FFFFFF;margin:16px 16px 0 16px ;border-radius: 2px 2px 0 0">

      <a-row type="flex" justify="space-between" align="middle"
             style="width:100%;padding: 16px 16px 16px 16px;background: #FFFFFF;">

        <!--表格选项-->
        <div style="color: rgba(0,0,0,.85);font-size: 16px;">区域信息</div>

        <div style="flex: 1 1 0"></div>

        <a-tooltip placement="top">
          <template #title>
            <span>保存</span>
          </template>
          <save-outlined :style="{fontSize: '16px'}"/>
        </a-tooltip>

        <a-divider type="vertical"/>

        <a-tooltip placement="top">
          <template #title>
            <span>还原</span>
          </template>
          <reload-outlined :style="{fontSize: '16px'}"/>
        </a-tooltip>

        <a-divider type="vertical"/>

        <a-tooltip placement="top">
          <template #title>
            <span>删除</span>
          </template>
          <delete-outlined :style="{fontSize: '16px'}"/>
        </a-tooltip>

      </a-row>

      <!--区域信息表单-->
      <a-form
          ref="defForm"
          :style="{background: '#FFFFFF',paddingLeft:'16px'}"
          :model="form.def.data"
          :rules="form.def.rules">
        <a-row :gutter="24" justify="start" style="margin-top: 16px;margin-right: 16px">
          <!--第一行-->
          <a-col :span="4">
            <a-form-item label="名称" name="name">
              <a-input v-model:value="form.def.data.name" placeholder="名称" style="font-size: 16px"/>
            </a-form-item>
          </a-col>
          <a-col :span="4">
            <a-form-item label="编号" name="code">
              <a-input v-model:value="form.def.data.code" placeholder="编号"/>
            </a-form-item>
          </a-col>

          <a-col :span="4">
            <a-form-item label="楼层" name="floor">
              <a-input v-model:value="form.def.data.floor" :min="1" placeholder="楼层"/>
            </a-form-item>
          </a-col>

          <a-col :span="4">
            <a-form-item label="第三方ID" name="sdk_id">
              <a-input v-model:value="form.def.data.sdk_id" :min="1" placeholder="第三方ID"/>
            </a-form-item>
          </a-col>
          <a-col :span="4">
            <a-form-item label="负责人ID" name="leader_user_id">
              <a-input type="number" v-model:value="form.def.data.leader_user_id" :min="1" placeholder="负责人ID"/>
            </a-form-item>
          </a-col>
          <a-col :span="4">
            <a-form-item label="排序" name="sort">
              <a-input type="number" v-model:value="form.def.data.sort" :min="1" placeholder="排序"/>
            </a-form-item>
          </a-col>


          <!--第二行-->
          <a-col :span="4">
            <a-form-item label="商家数量" name="store_count">
              <a-input type="number" v-model:value="form.def.data.store_count" :min="1" placeholder="商家数量"/>
            </a-form-item>
          </a-col>

          <a-col :span="4">
            <a-form-item label="最大滞留人数" name="max_stay_people">
              <a-input type="number" v-model:value="form.def.data.max_stay_people" :min="1" placeholder="最大滞留人数"/>
            </a-form-item>
          </a-col>

          <a-col :span="4">
            <a-form-item label="最大滞留时间，单位秒" name="max_stay_time">
              <a-input type="number" v-model:value="form.def.data.max_stay_time" :min="1" placeholder="最大滞留时间"/>
            </a-form-item>
          </a-col>

          <a-col :span="4">
            <a-form-item label="无效滞留时间" name="invalid_stay_time">
              <a-input type="number" v-model:value="form.def.data.invalid_stay_time" placeholder="无效滞留时间"/>
            </a-form-item>
          </a-col>
          <a-col :span="8">
            <a-form-item label="描述信息" name="desc">
              <a-input v-model:value="form.def.data.desc" placeholder="描述信息"/>
            </a-form-item>
          </a-col>


          <a-col :span="12">
            <a-form-item label="区域类型(0普通区域；1商家；2路段)" name="type">
              <a-input v-model:value="form.def.data.type" placeholder="区域类型"/>
            </a-form-item>
          </a-col>


          <a-col :span="12">
            <a-form-item label="区域属性(0顾客活动区；1商品区；2餐饮区；3交易区；4危险区)" name="area_type">
              <a-input v-model:value="form.def.data.area_type" placeholder="区域属性"/>
            </a-form-item>
          </a-col>


          <a-col :span="24">
            <a-form-item label="区域坐标(json数组)" name="area_xy">
              <a-textarea v-model:value="form.def.data.area_xy" placeholder="区域坐标"/>
            </a-form-item>
          </a-col>
          <a-col :span="24">
            <a-form-item label="属性配置(json对象)" name="config">
              <a-textarea v-model:value="form.def.data.config" placeholder="属性配置"/>
            </a-form-item>
          </a-col>
        </a-row>

      </a-form>

    </a-row>

    <!--新增Dialog-->
    <a-modal
        title="新增菜单"
        width="70%"
        v-model:visible="show.addDef"
        :destroyOnClose="true"
        :maskClosable="false">

      <!--新增表单-->
      <a-form
          ref="addForm"
          :model="form.add.data"
          :rules="form.add.rules">

        <a-form-item label="名称" name="name">
          <a-input v-model:value="form.add.data.name" placeholder="名称" style="font-size: 16px"/>
        </a-form-item>
        <a-form-item label="编号" name="code">
          <a-input v-model:value="form.add.data.code" placeholder="编号"/>
        </a-form-item>
        <a-form-item label="区域类型" name="type">
          <a-input v-model:value="form.add.data.type" placeholder="区域类型(0普通区域；1商家；2路段)"/>
        </a-form-item>
        <a-form-item label="区域属性" name="area_type">
          <a-input v-model:value="form.add.data.area_type" placeholder="区域属性(0顾客活动区；1商品区；2餐饮区；3交易区；4危险区)"/>
        </a-form-item>
        <a-form-item label="区域坐标(json数组)" name="area_xy">
          <a-textarea v-model:value="form.add.data.area_xy" placeholder="区域坐标"/>
        </a-form-item>
        <a-form-item label="属性配置(json对象)" name="config">
          <a-textarea v-model:value="form.add.data.config" placeholder="属性配置"/>
        </a-form-item>
        <a-divider/>

      </a-form>

      <!--底部按钮-->
      <template #footer>
        <a-button @click="show.addDef = false">取消</a-button>
        <a-button class="" @click="()=>{resetForm_('addForm')}">重置</a-button>
        <a-button class="" type="primary" @click="add_()">保存</a-button>
      </template>

    </a-modal>

  </div>
</template>

<script lang="ts">

import {notification} from 'ant-design-vue';
import {getCenterPosition} from "@/common/util/kit";
import {fabric} from 'fabric'
import peopleImg from "@/common/assets/png/people.png";

export default {
  async mounted() {

    this.intiCanvas();

    await this.queryAreaMap()

    this.initPeople()

    //监控窗口大小变更
    window.onresize = () => {
      // this.$refs.rightHeight.$el.height = 200;
      this.resizeCanvas()
    }

    //监控菜单关闭
    // this.$watch(() => $store.state.closeLeftSider, () => {
    //   setTimeout(() => {
    //     // this.$refs.rightHeight.$el.height = 200;
    //     this.resizeCanvas()
    //   }, 300)
    // })

    //监控菜单关闭
    // this.$watch(() => $store.state.closeLeftSider, () => {
    //   setTimeout(() => {
    //     // this.$refs.rightHeight.$el.height = 200;
    //     this.resizeCanvas()
    //   }, 300)
    // })

    this.resizeCanvas()

  },
  //数据
  data() {
    return {
      //搜索
      search: {
        //默认搜索
        def: {
          name: undefined,
          type: undefined,
          floor: 1,
        },
        defRules: {},
      },

      //表单
      form: {
        //默认表单
        def: {
          data: {},
          layout: 'inline', // 'horizontal' | 'vertical' | 'inline';
          //(初始化/重置)Data
          resetData: {
            desc: null,
          },
          rules: {
            pname: [
              {
                type: 'any',
                required: true,
                message: '必填项',
                trigger: 'blur',
              },
            ],
          },
        },
        //新增表单
        add: {
          data: {},
          layout: 'horizontal', // 'horizontal' | 'vertical' | 'inline';
          //(初始化/重置)Data
          resetData: {
            name: null,
            code: "0",
            type: "0",
            area_type: "0",
            area_xy: JSON.stringify([{"x": "1200", "y": "100"}, {"x": "1500", "y": "100"}, {
              "x": "1500",
              "y": "600"
            }, {"x": "1200", "y": "600"}]),
            config: JSON.stringify({
              "borderColor": "#8396A8",
              "backgroundColor": "#AAB4CA",
              "textSize": "150",
              "showText": true,
              "borderSize": "8",
              "showBorder": true,
              "textColor": "#000000",
              "showArea": true,
              "isBottom": false,
              "isSelect": false,
              "isTop": false,
              "textX": "0",
              "text": "",
              "textY": "0"
            }),
          },
          rules: {
            pname: [
              {
                type: 'any',
                required: true,
                message: '必填项',
                trigger: 'blur',
              },
            ],
          },
        },
      },

      //option选项
      option: {
        autoSearchName: [],
        areaType: [
          {key: '0', text: '普通区域'},
          {key: '1', text: '商家'},
          {key: '2', text: '路段'},
        ]
      },

      //开关
      show: {
        addDef: false,
        areaInfo: false,
        warn: false,
        people: true,
        track: true,
      },

      canvas: null,
      canvasConfig: {
        width: 1200,
        height: 600,
        mutiNum: 60,
        panning: false,
        data: {},
        trackLine: null,
        trackPosition: [{x: 10, y: 20}],
        background: '#E4E6E3',
        defaultZoom: 1,
        scale: 0.001,
        minScale: 0.006,
        maxScale: 3,
      },
      peopleXY: [],
      people: [],
      peopleTrack: [],
      //闪烁动画定时器
      flickerTimer: null,
    }
  },
  //方法
  methods: {
    //
    initPeople() {
      let peopleCount = 2;

      //6个初始坐标
      for (let i = 0; i < peopleCount; i++) {
        let x = Math.random() * 170
        let y = Math.random() * 70 * -1
        this.peopleXY.push([{x: x, y: y}])
      }

      //轨迹线
      for (let i = 0; i < peopleCount; i++) {
        let trackLine = new fabric.Polyline([], {
          stroke: '#763288',
          fill: 'transparent',
          strokeDashArray: [1],
          strokeWidth: 0.5,
          selectable: false,
          visible: true,
          objectCaching: false,
          hasControls: false,
        })
        this.canvas.add(trackLine)
        this.canvas.bringToFront(trackLine)
        this.peopleTrack.push(trackLine)
        // this.canvas.renderAll()
      }

      //小人图标
      for (let i = 0; i < peopleCount; i++) {
        let xy = this.peopleXY[i][0]
        fabric.Image.fromURL(peopleImg, graph => {
          graph.scale(0.015)
          graph.left = xy.x
          graph.top = xy.y
          graph.hasControls = false
          this.canvas.add(graph)
          this.canvas.bringToFront(graph)
          this.people.push(graph)
        })
      }

      //定时改变位置
      setInterval(() => {
        //console.log("setInterval")
        //更新坐标
        for (let i = 0; i < 1; i++) {
          let index = Math.round(Math.random() * (peopleCount - 1))
          //console.log(index)
          //console.log(this.people[index])

          let top = this.people[index].top
          let left = this.people[index].left

          let a = Math.round(Math.random() * 10)
          if (a >= 5) {
            top += 2
            left -= 3
          } else {
            top -= 3
            left += 2
          }

          //
          if (left > 170) {
            left = 169
          }
          if (left < -10) {
            left = -9
          }

          if (top < -70) {
            top = -69
          }
          if (top > 10) {
            top = 9
          }

          this.people[index].top = top
          this.people[index].left = left
          this.peopleTrack[index].points.push({x: left, y: top})
          // this.canvas.bringToFront(this.peopleTrack[index])
          this.canvas.requestRenderAll()

          //检查并报警
          this.warn(this.people[index])
        }

      }, 500)

    },
    //检查并报警
    warn(graphObj) {
      if (this.show.warn) {

        let top = graphObj.top
        let left = graphObj.left

        if ((left > 79 && left < 171) && (top > -47 && top < -1)) {

          let objs = this.canvas.getObjects()

          for (let i = 0; i < objs.length; i++) {

            let e = objs[i]
            if (e.data && e.data.id && e.data.id == 20) {
              notification.close("MarketMonitor")
              notification['warning']({
                key: "MarketMonitor",
                message: `警告：围栏区存在异常`,
                description: '围栏区异常',
                placement: 'topRight',
              });
              this.flicker(e, 800, 5, "#d00d0d", e.fill)
              break;
            }

          }
        }
      }
    },
    //小人：显示/隐藏
    changeShoPeople(isShow) {
      ////console.log("changeShoPeople", isShow)
      if (!isShow) {
        this.people.filter(e => {
          e.set('visible', false)
          this.canvas.renderAll()
        })
        this.peopleTrack.filter(e => {
          e.set('visible', false)
          this.canvas.renderAll()
        })
      }

      if (isShow) {
        this.people.filter(e => {
          e.set('visible', true)
          this.canvas.bringToFront(e)
          this.canvas.renderAll()
        })

        if (this.show.track) {
          this.peopleTrack.filter(e => {
            e.set('visible', true)
            this.canvas.renderAll()
          })
        }
      }

    },
    //轨迹：显示/隐藏
    changeShoTrack(isShow) {
      if (!isShow) {
        this.peopleTrack.filter(e => {
          e.set('visible', false)
          this.canvas.bringToFront(e)
          this.canvas.renderAll()
        })
      }
      if (isShow) {
        this.peopleTrack.filter(e => {
          e.set('visible', true)
          this.canvas.bringToFront(e)
          this.canvas.renderAll()
        })
      }
    },

    mouseenter() {
      document.body.style.overflow = 'hidden';
    },
    mouseleave() {
      document.body.style.overflow = '';
    },

    //查询：页面数据
    queryPage_() {
      if (!this.search.def.name && !this.search.def.type) {
        this.$message.warn("操作失败：请先输入名称或选择类型", 2)
        return;
      }

      let param = {
        ...this.search.def
      }

      this.$http.post('/position/area/page', param).then((res) => {
        if (res.status) {
          if (res.data.length == 0) {
            this.$message.success("未查询到任何信息")
            return;
          }

          //
          let ids = []
          res.data.filter(item => {
            ids.push(item.id)
          })

          //筛选
          let objs = this.canvas.getObjects()
          objs.filter(target => {
            if (ids.indexOf(target.id) != -1) {
              this.flicker(target, 800, 5, "#d00d0d", target.fill)
            }
          })

        } else {
          this.$message.warning(res.message)
        }
      })
    },

    //闪烁
    /**
     * TODO 抽成工具类
     * @param graphObj 画布对象
     * @param time 变色持续时间，毫秒
     * @param cycleCount 循环次数
     * @param flickerFill 闪烁时颜色
     * @param revertFill 还原颜色
     */
    flicker(graphObj, time, cycleCount, flickerFill, revertFill) {
      if (this.flickerTimer == null) {
        let canvas = this.canvas
        let count = 1;
        //闪烁
        this.flickerTimer = setInterval(() => {
          graphObj.set('fill', revertFill);

          graphObj.animate('fill', flickerFill, {
            onChange: canvas.renderAll.bind(canvas),
            onComplete: () => {
              count += 1;
              if (count >= cycleCount) {
                clearInterval(this.flickerTimer)
                graphObj.set('fill', revertFill);
                canvas.renderAll()
                this.flickerTimer = null
              }
            },
            duration: time
          });
        }, time)
      }
    },

    openAdd_() {
      this.show.addDef = true
      this.form.add.data = JSON.parse(JSON.stringify(this.form.add.resetData))
    },

    //canvas：查询区域信息
    add_() {
      this.$refs.addForm
          .validate()
          .then(() => {
            this.$http.post("/position/area/addArea", this.form.add.data).then((res) => {
              if (res.status) {
                this.$message.success(res.message)
                this.queryAreaMap()
                this.show.addDef = false
              } else {
                this.$message.warning(res.message)
              }
            })
          })
          .catch(error => {
            this.$message.warning("存在错误，请检查表单填写情况。", 2)
          });
    },

    async queryAreaMap() {
      this.canvas.clear()
      this.canvas.renderAll()
      return this.$http.post('/position/area/map', {}).then((res) => {
        if (res.status) {

          let area = []
          let store = []
          let road = []

          // 区域类型(0普通区域；1商家；2路段)
          res.data.area.filter(item => {
            if (item.type == 0) {
              area.push(item)
            }
            if (item.type == 1) {
              store.push(item)
            }
            if (item.type == 2) {
              road.push(item)
            }
          })

          // //////console.log("maps", maps)
          // //////console.log("area", area)
          // //////console.log("store", store)
          // //////console.log("road", road)
          // //////console.log("maps", maps)
          let maps = []
          maps.push(...area)
          maps.push(...store)
          maps.push(...road)
          this.drawMap(maps)
        } else {
          this.$message.warning(res.message)
        }
      })
    },

    //canvas：绘制区域
    drawMap(datas) {
      for (let i = 0; i < datas.length; i++) {

        let data = datas[i]
        //////console.log(data)
        let config = JSON.parse(data.config)
        let xyArray = JSON.parse(data.area_xy)

        //画区域
        let graphPath
        if (config && config.showArea) {

          //起点
          let M_X = parseFloat(xyArray[0].x)
          let M_Y = parseFloat(xyArray[0].y)
          let pathStr = 'M ' + M_X + ' ' + (M_Y * -1)
          //循环追加点位
          for (let i = 1; i < xyArray.length; i++) {
            let L_X = parseFloat(xyArray[i].x)
            let L_Y = parseFloat(xyArray[i].y)
            pathStr += ' L ' + L_X + ' ' + (L_Y * -1)
          }
          //终止
          pathStr += ' z'

          //绘制
          graphPath = new fabric.Path(pathStr, {
            fill: config.backgroundColor,
            selectable: true,
            hasControls: false,
            lockMovementX: true,
            lockMovementY: true,
            //自定义属性
            data: data,
          })

          //边框
          if (config.showBorder) {
            //边框颜色
            if (config.borderColor) {
              graphPath.stroke = config.borderColor
            }
            //边框宽度
            if (config.borderSize) {
              graphPath.strokeWidth = parseFloat(config.borderSize)
            }
          }
          this.canvas.add(graphPath)

          //向上到顶层
          if (config.isTop) {
            this.canvas.bringToFront(graphPath)
          }

          //向下到底层
          if (config.isBottom) {
            this.canvas.sendToBack(graphPath)
          }

        }

        //加文字
        if (config && config.showArea && config.showText) {
          // if (0==1) {
          //////console.log(5)

          //计算中心坐标
          let pathS = []
          for (let k = 0; k < graphPath.path.length - 1; k++) {
            let path_ = graphPath.path[k]
            pathS.push({x: path_[1], y: path_[2]})
          }
          let centerPosition = getCenterPosition(pathS)

          //绘制
          let graphText = new fabric.Textbox(data.name, {
            fontSize: config.textSize * 1,
            hasControls: false,
            lockMovementX: true,
            lockMovementY: true,
            selectable: false,
            //自定义属性
            data: data,
          })

          //中心坐标
          graphText.top = centerPosition.centerY - graphText.height
          graphText.left = centerPosition.centerX - (graphText.width / 2)

          //文字宽度
          if (config.textWidth) {
            graphText.width = parseFloat(config.textWidth)
            graphText.splitByGrapheme = true
          }

          //文字颜色
          if (config.textColor) {
            graphText.fill = config.textColor
          }

          //坐标
          if (config.textX) {
            graphText.left = parseFloat(config.textX)
          }
          if (config.textY) {
            graphText.top = parseFloat(config.textY)
          }

          //偏移量
          if (config.textXOffset) {
            graphText.left = graphText.left + parseFloat(config.textXOffset)
          }
          if (config.textYOffset) {
            graphText.top = graphText.top + (parseFloat(config.textYOffset) * -1)
          }

          this.canvas.add(graphText)
        }
      }
    },

    //区域信息
    areaInfo(event) {
      if (event.target && event.target.data) {
        this.form.def.data = event.target.data
      }
      ////console.log("areaInfo:event", event)
    },

    //查询：异步搜索完成查询
    searchAsync_(key, value) {
      //////console.log("searchAsync, key=" + key + ' , value=' + value)
      this.$http.post('/position/area/searchInput', {[key]: value}).then(res => {
        this.option.autoSearchName = res.data
      })
    },

    //查询：异步搜索点击选择事件
    searchAsyncSelect_(key, value, item) {
      ////console.log("searchAsyncSelect_, key " + key)
      ////console.log("searchAsyncSelect_, value " + value)
      ////console.log("searchAsyncSelect_, item " + item)
      this.search.def.name = value
    },

    //重置：查询表单重置
    resetForm_(formName) {
      if (this.$refs[formName]) {
        this.$refs[formName].resetFields()
      }
    },
    //
    fullScreen() {
    },
    //canvas：初始化
    intiCanvas() {
      this.canvas = new fabric.Canvas('canvas', {
        selection: false
        // originY: 'bottom'
      })
      this.canvas.setWidth(this.$refs.canvasSize.offsetWidth)
      this.canvas.setHeight(this.$refs.canvasSize.offsetHeight)
      this.canvas.preserveObjectStacking = true

      // 鼠标按下事件
      this.canvas.on('mouse:down', (event) => {
        this.areaInfo(event)
        this.canvasConfig.panning = true
        //////console.log("event.target", event.target)
        // this.flicker(true, event.target)
      })

      // 鼠标抬起事件
      this.canvas.on('mouse:up', (event) => {
        // ////console.log("canvas:mouse:up", event)
        this.canvasConfig.panning = false
      })

      //移动画布事件
      this.canvas.on('mouse:move', (event) => {
        // //////console.log("canvas:mouse:move", event)
        if (this.canvasConfig.panning && event && event.e) {
          let delta = new fabric.Point(event.e.movementX, event.e.movementY)
          this.canvas.relativePan(delta)
        }
      })

      // 监听鼠标滚轮事件
      this.canvas.on('mouse:wheel', opt => {
        // 滚轮向上滚一下是 -100，向下滚一下是 100
        let delta = opt.e.deltaY
        // 获取画布当前缩放值
        let zoom = this.canvas.getZoom()
        zoom -= 0.005 * delta
        if (zoom > 20) zoom = 20
        if (zoom < 1) zoom = 1

        // 设置画布缩放比例
        this.canvas.zoomToPoint(
            {
              x: opt.e.offsetX,
              y: opt.e.offsetY
            },
            zoom
        )
      })

    },
    //canvas：缩放
    setZoom(zoomNum) {
      let zoom = zoomNum + (zoomNum === 0 ? 1 : this.canvas.getZoom())
      zoom = Math.max(this.canvasConfig.defaultZoom, zoom) // 最小为原来
      zoom = Math.min(3, zoom) // 最大是原来的3倍
      const zoomPoint = new fabric.Point(0, 0)
      this.canvas.zoomToPoint(zoomPoint, zoom)
    },
    //canvas：布局大小变更
    resizeCanvas() {
      let canvas = this.canvas;
      //
      canvas.setWidth(this.$refs.canvasSize.offsetWidth)
      canvas.setHeight(this.$refs.canvasSize.offsetHeight)
      // 计算
      const [minX, minY, maxX, maxY] = this.calcBorder()
      // //////console.log("minX",minX)
      // //////console.log("minY",minY)
      // //////console.log("maxX",maxX)
      // //////console.log("maxY",maxY)

      // 平移
      const panX = (maxX - minX - canvas.width) / 2 + minX
      const panY = (maxY - minY - canvas.height) / 2 + minY
      canvas.absolutePan({x: panX, y: panY})

      //缩放
      let zoom = Math.min(canvas.width / (maxX - minX), canvas.height / (maxY - minY))

      //计算缩放中心
      let zoomPoint = new fabric.Point(canvas.width / 2, canvas.height / 2)

      //开始缩放
      canvas.zoomToPoint(zoomPoint, zoom)
      this.canvasConfig.defaultZoom = zoom
    },
    //canvas：计算布局大小变更宽高
    calcBorder() {
      let canvas = this.canvas
      //先还原缩放比例与位置
      canvas.setZoom(1)
      canvas.absolutePan({x: 0, y: 0})

      //遍历所有显示对象，获取最小坐标，最大坐标
      let objects_ = canvas.getObjects()
      let objects = []
      objects_.filter(e => {
        if (e.visible) {
          objects.push(e)
        }
      })

      // //////console.log(`"objects", objects)
      let minX = 0
      let minY = 0
      let maxX = canvas.width
      let maxY = canvas.height

      if (objects.length > 0) {
        let rect = objects[0].getBoundingRect()
        minX = rect.left
        minY = rect.top
        maxX = rect.left + rect.width
        maxY = rect.top + rect.height
        for (let i = 1; i < objects.length; i++) {
          rect = objects[i].getBoundingRect()
          minX = Math.min(minX, rect.left)
          minY = Math.min(minY, rect.top)
          maxX = Math.max(maxX, rect.left + rect.width)
          maxY = Math.max(maxY, rect.top + rect.height)
        }
      }
      return [minX, minY, maxX, maxY]
    },
  }
}

</script>

<style lang="less">
</style>

<style scoped lang="less">

::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}

::-webkit-scrollbar-thumb {
  background: rgba(0, 0, 0, .12);
  border-radius: 3px;
  -webkit-box-shadow: inset 0 0 5px rgb(0 21 41 / 5%);
}

::-webkit-scrollbar-track {
  background: rgba(0, 0, 0, .06);
}
</style>
